package xin.ctkqiang.controller;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.DumperOptions.FlowStyle;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.representer.Representer;

import com.fasterxml.jackson.databind.ObjectMapper;

import xin.ctkqiang.config.Configuration;
import xin.ctkqiang.dto.Database;
import xin.ctkqiang.dto.Exploit;

/**
 * 数据库控制器类
 * 负责处理与数据库相关的所有操作，包括：
 * - 数据库连接管理
 * - 数据表的创建和维护
 * - 数据的增删改查
 * - 数据导出功能（支持CSV、JSON、SQL格式）
 */
public class DatabaseController {
    /** 数据库名称 */
    protected final static String DB_NAME = Configuration.DB_NAME;

    /** MYSQL 数据库连接URL */
    protected final static String DB_URL = Configuration.DB_URL;

    /** 数据库用户名 */
    protected final static String DB_USER = Configuration.DB_USER;

    /** 数据库密码 */
    protected final static String DB_PASSWORD = Configuration.DB_PASSWORD;

    /** 数据库驱动类名 */
    protected final static String DB_DRIVER = Configuration.DB_DRIVER;

    /** SQLITE 数据库连接URL */
    protected final static String DB_SQLITE_URL = Configuration.DB_SQLITE_URL;

    /**
     * 静态初始化块
     * 在类加载时尝试加载数据库驱动
     */
    static {
        try {
            Class.forName(DB_DRIVER);
        } catch (ClassNotFoundException e) {
            System.err.println("数据库驱动加载失败：" + e.getMessage());
        }
    }

    /**
     * 获取数据库连接
     * 
     * @return 数据库连接对象
     * @throws SQLException 如果连接数据库时发生错误
     */
    protected Connection getConnection() throws SQLException {
        String mode = Configuration.getDatabaseMode();
        String upperMode = mode.toUpperCase();
        System.out.println("💡 切换数据库模式 -> " + upperMode);

        switch (upperMode) {
            case "SQLITE":
                try {
                    Class.forName("org.sqlite.JDBC");
                    System.out.println("📦 嘻嘻～SQLite 驱动已加载好惹！我已经抱好数据库小可爱啦～ฅ(＾・ω・＾ฅ)");
                } catch (ClassNotFoundException e) {
                    System.err.println("❌ 哎呀呀！SQLite JDBC 驱动不见惹～人家找不到驱动怎么贴贴数据库嘛喵呜呜｡ﾟ(ﾟ´ω`ﾟ)ﾟ｡");
                    throw new SQLException("找不到 SQLite 驱动喵～要不要检查一下依赖有没有加对呀？");
                }
                return DriverManager.getConnection(DatabaseController.DB_SQLITE_URL);

            case "MYSQL":
            default:
                try {
                    Class.forName(DatabaseController.DB_DRIVER);
                    System.out.println("📦 MySQL 驱动加载完成！我打扮好了，要去连接数据库小哥哥啦～(๑•̀ㅂ•́)و✧");
                } catch (ClassNotFoundException e) {
                    System.err.println("❌ 呜呜呜 MySQL JDBC 驱动不见了～是不是打包的时候忘记带灵儿一起走啦 >///< ");
                    throw new SQLException("MySQL 驱动加载失败了喵～快检查一下 `.jar` 有没有遗漏吧！");
                }
                return DriverManager.getConnection(
                        DatabaseController.DB_URL,
                        DatabaseController.DB_USER,
                        DatabaseController.DB_PASSWORD);
        }
    }

    /**
     * 安全关闭数据库资源
     * 支持批量关闭多个资源对象（如Connection、Statement、ResultSet等）
     * 
     * @param resources 需要关闭的资源对象列表
     */
    protected void close(AutoCloseable... resources) {
        for (AutoCloseable res : resources) {
            if (res != null) {
                try {
                    res.close();
                } catch (Exception e) {
                    System.err.println("资源关闭失败：" + e.getMessage());
                }
            }
        }
    }

    /**
     * 创建数据表（如果不存在）
     * 表结构包含以下字段：
     * - id: 漏洞ID，主键
     * - description: 漏洞描述
     * - date: 发布日期
     * - author: 作者
     * - type: 漏洞类型
     * - platform: 影响平台
     * - created_at: 记录创建时间
     */
    public void CreateTableIfNotExists() {
        // 构建建表SQL语句
        String query = "CREATE TABLE IF NOT EXISTS records ("
                + "id VARCHAR(64) NOT NULL,"
                + "description TEXT,"
                + "date VARCHAR(20),"
                + "author VARCHAR(100),"
                + "type VARCHAR(50),"
                + "platform VARCHAR(50),"
                + "created_at DATETIME DEFAULT CURRENT_TIMESTAMP,"
                + "PRIMARY KEY (id)"
                + ");";

        try {

            String mode = Configuration.getDatabaseMode();
            Database dbMode;

            try {
                dbMode = Database.fromValue(mode);
            } catch (IllegalArgumentException e) {
                dbMode = null;
            }

            if (dbMode == null) {
                System.out.println("🥴 吃嗯的！这个数据库模式不认识喔：\"" + mode + "\" 妹妹我拒绝连接！");
                return;
            }

            switch (dbMode) {
                case SQLITE:
                    // 加载 SQLite JDBC 驱动
                    Class.forName("org.sqlite.JDBC");

                    // 构建连接（数据库文件叫 ling.sqlite）
                    try (Connection conn = DriverManager.getConnection(DatabaseController.DB_SQLITE_URL);
                            Statement stmt = conn.createStatement()) {
                        stmt.execute(query);
                        System.out.println("🎀 SQLite 模式下，表结构初始化好啦！已经准备好可爱的记录了喵～");
                    }

                    break;
                case MYSQL:
                default:
                    // 确保数据库驱动已加载
                    Class.forName(DatabaseController.DB_DRIVER);

                    // 创建数据库连接并执行建表语句
                    try (Connection conn = DriverManager.getConnection(
                            DatabaseController.DB_URL,
                            DatabaseController.DB_USER,
                            DatabaseController.DB_PASSWORD);
                            Statement stmt = conn.createStatement()) {
                        stmt.execute(query);
                        System.out.println("💾 MySQL 模式下，数据表【内容信息】初始化成功～♡");
                    }
            }

        } catch (Exception e) {
            System.err.println("❌ 表创建失败惹呜呜呜呜：妹抖崩坏 > <\n" + e.getMessage());
            e.printStackTrace();
        }
    }

    /**
     * 批量添加漏洞信息到数据库
     * 使用批处理方式提高插入效率
     * 
     * @param exploit_list 要添加的漏洞信息列表
     * @return 操作状态码（0表示完成）
     */
    public int AddExploit(List<Exploit> exploit_list) {
        // 构建插入SQL语句
        StringBuilder stringBuilder = new StringBuilder();

        int[] BatchResults;
        long SuccessCount = 0;

        // 准备参数化SQL语句
        stringBuilder.append("INSERT INTO records (id, description, date, author, type, platform) ");
        stringBuilder.append("VALUES (?, ?, ?, ?, ?, ?)");

        // 确保输入列表不为空
        assert exploit_list != null;

        try {
            final String InsertSQL = stringBuilder.toString();

            // 创建数据库连接和预处理语句
            try (Connection conn = getConnection();
                    java.sql.PreparedStatement PSTMT = conn.prepareStatement(InsertSQL)) {

                // 确保连接和语句对象创建成功
                assert conn != null && PSTMT != null;

                // 遍历漏洞列表，设置参数并添加到批处理
                for (Exploit exploit : exploit_list) {
                    PSTMT.setString(1, exploit.getId());
                    PSTMT.setString(2, exploit.getDescription());
                    PSTMT.setString(3, exploit.getDate());
                    PSTMT.setString(4, exploit.getAuthor());
                    PSTMT.setString(5, exploit.getType());
                    PSTMT.setString(6, exploit.getPlatform());

                    PSTMT.addBatch();
                }

                // 执行批处理并统计成功数量
                BatchResults = PSTMT.executeBatch();
                SuccessCount = Arrays.stream(BatchResults).filter(r -> r >= 1).count();

            } catch (SQLException e) {
                System.err.println("SQL 语句创建失败：" + e.getMessage());
            } finally {
                // 输出操作结果
                System.out.println(
                        "✅ 成功写入/更新了 " + SuccessCount + "| " + this.GetSatusString(SuccessCount) + " 条 Exploit 数据");
            }

        } catch (Exception e) {
            System.err.println("添加失败：" + e.getMessage());
        }
        return 0;
    }

    /**
     * 获取数据库中所有漏洞信息
     * 按创建时间降序排序
     * 
     * @return 漏洞信息列表
     */
    public List<Exploit> GetAllExploits() {
        // 构建查询SQL
        final String query = "SELECT * FROM records ORDER BY created_at DESC";

        List<Exploit> exploits = new ArrayList<>();

        try {
            // 加载数据库驱动
            Class.forName(DB_DRIVER);

            // 创建数据库连接并执行查询
            try (Connection conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);
                    Statement stmt = conn.createStatement();
                    ResultSet rs = stmt.executeQuery(query)) {

                // 遍历结果集并构建Exploit对象
                while (rs.next()) {
                    Exploit exploit = new Exploit();

                    // 设置漏洞信息的各个字段
                    exploit.setId(rs.getString("id"));
                    exploit.setDescription(rs.getString("description"));
                    exploit.setDate(rs.getString("date"));
                    exploit.setAuthor(rs.getString("author"));
                    exploit.setType(rs.getString("type"));
                    exploit.setPlatform(rs.getString("platform"));

                    // 添加到结果列表
                    exploits.add(exploit);

                    // 确保对象创建成功
                    assert exploit != null;
                }
            } catch (SQLException e) {
                System.err.println("查询失败：" + e.getMessage());
            }
        } catch (ClassNotFoundException e) {
            System.err.println("数据库驱动加载失败：" + e.getMessage());
        }

        return exploits;
    }

    /**
     * 获取操作状态的描述信息
     * 
     * @param status 操作状态码
     * @return 状态描述
     */
    private String GetSatusString(long status) {
        if (status > 0) {
            return "成功插入或更新";
        } else if (status == 0) {
            return "没有任何变化";
        } else {
            return "未知状态";
        }
    }

    /**
     * 将漏洞信息导出为JSON格式
     * 使用Jackson库进行JSON序列化，支持格式化输出
     * 
     * @param exploits 要导出的漏洞信息列表
     * @return 成功导出的记录数量
     */
    public int ExportToJSON(List<Exploit> exploits) {
        if (DatabaseController.EnsureFolderExists("output")) {
            // 创建JSON对象映射器
            ObjectMapper mapper = new ObjectMapper();
            String filePath = this.FilePath(exploits.size(), ".json");

            try {
                // 使用美化打印器写入JSON文件
                mapper.writerWithDefaultPrettyPrinter().writeValue(new File(filePath), exploits);
                System.out.println("✅ JSON 导出成功: " + filePath);

                // 确保导出数据不为空
                assert exploits != null && exploits.size() > 0 : "导出列表不应为空";

                return exploits.size();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return 0;
    }

    public int ExportToYAML(List<Exploit> exploits) {
        if (DatabaseController.EnsureFolderExists("output")) {
            try {
                DumperOptions options = new DumperOptions();
                options.setIndent(2);
                options.setPrettyFlow(true);
                options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);

                Yaml yaml = new Yaml(options);

                String filePath = FilePath(exploits.size(), ".yaml");

                try (FileWriter writer = new FileWriter(filePath)) {
                    yaml.dump(exploits, writer);
                }

                System.out.println("✅ YAML 导出完成，文件位置: " + filePath);

                return 1;
            } catch (IOException e) {
                e.printStackTrace();
                return -1;
            }
        }

        return 0;
    }

    /**
     * 将漏洞信息导出为SQL插入语句
     * 生成可直接执行的INSERT语句
     * 
     * @param exploits 要导出的漏洞信息列表
     * @return 成功导出的记录数量
     */
    public int ExportToSQL(List<Exploit> exploits) {
        // 确保输出目录存在
        if (!DatabaseController.EnsureFolderExists("output")) {
            System.err.println("❌ 无法创建输出目录: " + "output");
            return 0;
        }

        String filePath = this.FilePath(exploits.size(), ".sql");

        try (BufferedWriter writer = new BufferedWriter(new FileWriter(filePath))) {
            // 写入SQL文件头部注释
            writer.write("-- ExploitDB 导出 SQL 插入语句\n\n");

            // 为每条记录生成INSERT语句
            for (Exploit e : exploits) {
                String sql = String.format(
                        "INSERT INTO exploits (id, description, date, author, type, platform) VALUES (%d, '%s', '%s', '%s', '%s', '%s');\n",
                        e.getId(),
                        EscapeSql(e.getDescription()),
                        e.getDate(),
                        EscapeSql(e.getAuthor()),
                        EscapeSql(e.getType()),
                        EscapeSql(e.getPlatform()));
                writer.write(sql);
            }

            writer.flush();
            System.out.println("✅ SQL 导出成功: " + filePath);
            return exploits.size();

        } catch (IOException e) {
            System.err.println("❌ 导出 SQL 文件失败！");
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 生成导出文件的路径
     * 格式：output/Exploits_[记录数]_[时间戳][扩展名]
     * 
     * @param size      记录数量
     * @param extension 文件扩展名
     * @return 完整的文件路径
     */
    private String FilePath(int size, String extension) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH:mm:ss");
        LocalDateTime now = LocalDateTime.now();

        return "output/Exploits_" + size + "_" + formatter.format(now) + extension;
    }

    /**
     * 将漏洞信息导出为CSV格式
     * 支持中文字段名，自动处理特殊字符
     * 
     * @param exploits 要导出的漏洞信息列表
     * @return 成功导出的记录数量
     */
    public int ExportToCSV(List<Exploit> exploits) {
        if (DatabaseController.EnsureFolderExists("output")) {
            String FilePath = this.FilePath(exploits.size(), ".csv");

            try (FileWriter Writer = new FileWriter(FilePath)) {
                // 写入CSV表头
                Writer.write("描述,日期,作者,类型,平台\n");

                // 写入每条记录
                for (Exploit e : exploits) {
                    Writer.write(CsvSafe(e.getDescription()) + "," +
                            CsvSafe(e.getDate()) + "," +
                            CsvSafe(e.getAuthor()) + "," +
                            CsvSafe(e.getType()) + "," +
                            CsvSafe(e.getPlatform()) + "\n");
                }

                System.out.println("✅ CSV 导出完成，保存路径：" + FilePath);

                // 确保导出数据不为空
                assert exploits != null && exploits.size() > 0 : "导出列表不应为空";

                return exploits.size();
            } catch (IOException e) {
                System.err.println("创建文件失败：" + e.getMessage());
                return 0;
            }
        }

        return 0;
    }

    /**
     * 转义SQL字符串中的特殊字符
     * 主要处理单引号，防止SQL注入
     * 
     * @param value 需要转义的字符串
     * @return 转义后的安全字符串
     */
    private static String EscapeSql(String value) {
        if (value == null) {
            return "";
        }
        return value.replace("'", "''"); // 将单引号替换为两个单引号
    }

    /**
     * 处理CSV字符串中的特殊字符
     * 包括：双引号、逗号、换行符
     * 
     * @param value 需要处理的字符串
     * @return 处理后的CSV安全字符串
     */
    private static String CsvSafe(String value) {
        if (value == null) {
            return "";
        }

        // 将双引号替换为两个双引号
        value = value.replace("\"", "\"\"");

        // 如果包含特殊字符，则用双引号包裹
        if (value.contains(",") || value.contains("\"") || value.contains("\n")) {
            value = "\"" + value + "\"";
        }

        return value;
    }

    /**
     * 确保指定的文件夹存在
     * 如果不存在则创建文件夹
     * 
     * @param folderPath 文件夹路径
     * @return 如果文件夹存在或创建成功返回true，否则返回false
     */
    private static boolean EnsureFolderExists(String folderPath) {
        File folder = new File(folderPath);

        if (!folder.exists()) {
            System.out.println("📂 目录不存在，正在创建: " + folderPath);
            return folder.mkdirs(); // 创建多级目录
        }

        return folder.isDirectory(); // 确保路径是一个目录
    }
}
